﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SecuritiesAnalysis.Common;
using SecuritiesAnalysis.Interface;
using SecuritiesAnalysis.CurrentData;

namespace SecuritiesAnalysis.MACalculator
{
    public class Calculator:ICalculator
    {
        private SecuritiesAnalysisDataContext ctx;
        private List<T_HistoryPrice> historyList;
        private List<T_Info_MA> MAList;
        private List<T_StockCode> CodeList;
        private DateTime latest;
        private T_Info_MA mamod;

        public Calculator()
        {
            this.ctx = new SecuritiesAnalysisDataContext();
        }

        #region ICalculator Members

        public bool Clear()
        {
            ctx.ExecuteCommand("TRUNCATE TABLE T_Info_MA");
            return true;
        }

        public bool ReCalculate()
        {
            Clear();

            CodeList = ctx.T_StockCodes.Where(c => c.Enabled == true).ToList();

            foreach (T_StockCode code in CodeList)
            {
                if (!ReCalculate(code.StockCode))
                {
                    return false;
                }
            }

            return true;
        }

        public bool Calculate()
        {
            CodeList = ctx.T_StockCodes.Where(c => c.Enabled == true).ToList();

            foreach (T_StockCode code in CodeList)
            {
                if (!Calculate(code.StockCode))
                {
                    return false;
                }
            }

            return true;
        }

        public bool CalculateToday()
        {
            CodeList = ctx.T_StockCodes.Where(c => c.Enabled == true).ToList();

            #region 判断是否数据库中有最新记录，如果没有则重新添加，希望能够提高效率

            latest = GetLatestHistoryDate(CodeList[1].StockCode);

            if (latest < DateTime.Today)
            {
                //获取并插入最新记录
                using (DataSpider ds = new DataSpider())
                {
                    ds.UpdateCurrentPrice();
                }
            }
            #endregion

            foreach (T_StockCode code in CodeList)
            {
                if (!CalculateToday(code.StockCode))
                {
                    return false;
                }
            }

            return true;
        }

        #endregion

        #region ICalculator Members

        public bool Clear(int stockcode)
        {
            try
            {
                //MAList = ctx.T_Info_MAs.Where(c => c.StockCode == stockcode).ToList();
                //ctx.T_Info_MAs.DeleteAllOnSubmit(MAList);
                //ctx.SubmitChanges();

                ctx.ExecuteCommand("DELETE FROM T_Info_MA WHERE [STOCKCODE]=" + stockcode);

                return true;
            }
            catch (Exception e)
            {
                Log.OutputError(e.Message);
                return false;
            }
        }

        public bool ReCalculate(int stockcode)
        {
            Clear(stockcode);

            try
            {
                historyList = ctx.T_HistoryPrices.Where(c=>c.StockCode == stockcode).OrderBy(c => c.Date).ToList();//按日期升序
                MAList = new List<T_Info_MA>();

                for (int i = 0; i < historyList.Count; i++)
                {
                    T_Info_MA mod = new T_Info_MA();
                    mod.StockCode = stockcode;
                    mod.Date = historyList[i].Date;
                    
                    mod.MA3=GetMA(3,i);
                    mod.MA5 = GetMA(5, i);
                    mod.MA21 = GetMA(21, i);

                    MAList.Add(mod);
                }

                ctx.T_Info_MAs.InsertAllOnSubmit(MAList);
                ctx.SubmitChanges();

                return true;
            }
            catch (Exception e)
            {
                Log.OutputError(e.Message);
                return false;
            }
        }

        public bool Calculate(int stockcode)
        {
            try
            {
                latest=GetLatestDate(stockcode);

                historyList = ctx.T_HistoryPrices.Where(c => c.StockCode == stockcode).OrderBy(c => c.Date).ToList();//按日期升序
                MAList = new List<T_Info_MA>();

                for (int i = 0; i < historyList.Count; i++)
                {
                    if (historyList[i].Date <= latest)
                    {
                        //过滤已经计算过的日期
                        continue;
                    }

                    T_Info_MA mod = new T_Info_MA();
                    mod.StockCode = stockcode;
                    mod.Date = historyList[i].Date;

                    mod.MA3 = GetMA(3, i);
                    mod.MA5 = GetMA(5, i);
                    mod.MA21 = GetMA(21, i);

                    MAList.Add(mod);
                }

                ctx.T_Info_MAs.InsertAllOnSubmit(MAList);
                ctx.SubmitChanges();

                return true;
            }
            catch (Exception e)
            {
                Log.OutputError(e.Message);
                return false;
            }

        }

        public bool CalculateToday(int stockcode)
        {
            try
            {
                latest = GetLatestHistoryDate(stockcode);
                
                if (latest < DateTime.Today)
                {
                    //获取并插入最新记录
                    using (DataSpider ds = new DataSpider())
                    {
                        ds.UpdateCurrentPrice(stockcode);
                    }
                }

                historyList = ctx.T_HistoryPrices.Where(c => c.StockCode == stockcode).OrderBy(c => c.Date).ToList();//按日期升序
                mamod = ctx.T_Info_MAs.Where(o => o.StockCode == stockcode && o.Date == DateTime.Today).FirstOrDefault();

                if (mamod == null)
                {
                    //如果不存在则插入
                    T_Info_MA mod = new T_Info_MA();
                    mod.StockCode = stockcode;
                    mod.Date = historyList[historyList.Count - 1].Date;

                    mod.MA3 = GetMA(3, historyList.Count - 1);
                    mod.MA5 = GetMA(5, historyList.Count - 1);
                    mod.MA21 = GetMA(21, historyList.Count - 1);

                    ctx.T_Info_MAs.InsertOnSubmit(mod);
                }
                else
                {
                    //如果已存在则更新
                    mamod.MA3 = GetMA(3, historyList.Count - 2);
                    mamod.MA5 = GetMA(5, historyList.Count - 2);
                    mamod.MA21 = GetMA(21, historyList.Count - 2);
                }

                ctx.SubmitChanges();

                return true;
            }
            catch (Exception e)
            {
                Log.OutputError(e.Message);
                return false;
            }
        }

        #endregion

        #region private methods

        /// <summary>
        /// 计算MA
        /// </summary>
        /// <param name="days">天数，如3/5/21</param>
        /// <param name="index">计算元素在historyList中的索引（此处设计的不怎么好，为了参数性能）</param>
        /// <returns>MA</returns>
        private decimal GetMA(int days,int index)
        {
            if (index+1<days)
            {
                return 0;
            }

            decimal result = historyList[index].ClosingPrice.Value;

            for (int i = 1; i < days; i++)
            {
                result += historyList[index - i].ClosingPrice.Value;
            }

            result = result / days;
            
            return result;
        }

        /// <summary>
        /// 获取当前某股票已经计算的最新MA日期
        /// </summary>
        /// <param name="stockcode">股票代码</param>
        /// <returns>最新日期</returns>
        private DateTime GetLatestDate(int stockcode)
        {
            try
            {
                T_Info_MA latest = ctx.T_Info_MAs.Where(o => o.StockCode == stockcode).OrderByDescending(o => o.Date).First();
                return latest.Date;
            }
            catch (Exception e)
            {
                if (e.Message == "Sequence contains no elements")
                {
                    return DateTime.MinValue;
                }
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// 获取当前某股票最新历史数据
        /// </summary>
        /// <param name="stockcode">股票代码</param>
        /// <returns>最新历史数据</returns>
        private DateTime GetLatestHistoryDate(int stockcode)
        {
            try
            {
                T_HistoryPrice latest = ctx.T_HistoryPrices.Where(o => o.StockCode == stockcode).OrderByDescending(o => o.Date).First();
                return latest.Date;
            }
            catch (Exception e)
            {
                if (e.Message == "Sequence contains no elements")
                {
                    return DateTime.MinValue;
                }
                return DateTime.MinValue;
            }
        }

        #endregion

    }
}
